"""Auth module for cherrypy.
Taken from http://tools.cherrypy.org/wiki/AuthenticationAndAccessRestrictions
"""

import sqlite3 as sqlite
from constants import DATABASE_FILE, USERNAME_SESSION_KEY
import md5
import cherrypy

def check_credentials(username, password):
  """Verifies credentials for username and password.
  Returns None on success or a string describing the error on failure.
  """
  connection = sqlite.connect(DATABASE_FILE)

  command = "SELECT password FROM users WHERE username = ?"
  answer = connection.execute(command, (username, )).fetchone()
  if not answer:
    return "No user with login %s." % username

  if answer[0] != md5.new(password).hexdigest():
    return "Incorrect password."
  
def check_auth(*args, **kwargs):
  """A tool that looks in config for 'auth.require'. If found and it
  is not None, a login is required and the entry is evaluated as a list of
  conditions that the user must fulfill"""
  conditions = cherrypy.request.config.get('auth.require', None)
  if conditions is not None:
    username = cherrypy.session.get(USERNAME_SESSION_KEY)
    if username:
      cherrypy.request.login = username
      for condition in conditions:
        # A condition is just a callable that returns true or false
        if not condition():
          raise cherrypy.HTTPRedirect("/auth/login")
    else:
      raise cherrypy.HTTPRedirect("/auth/login")
    
cherrypy.tools.auth = cherrypy.Tool('before_handler', check_auth)

def require(*conditions):
  """A decorator that appends conditions to the auth.require config
  variable."""
  def decorate(f):
    if not hasattr(f, '_cp_config'):
      f._cp_config = dict()
    if 'auth.require' not in f._cp_config:
      f._cp_config['auth.require'] = []
    f._cp_config['auth.require'].extend(conditions)
    return f
  return decorate


# Conditions are callables that return True if the user fulfills the conditions
# they define, False otherwise.
#
# They can access the current username as cherrypy.request.login.
#
# Define those at will however suits the application.
def admin():
  def check():
    connection = sqlite.connect(DATABASE_FILE)
    answer = connection.execute("SELECT is_admin FROM users WHERE username = ?",
                                (cherrypy.request.login, )).fetchone()
    if not answer:
      return False
    return answer[0]
  return check

# Controller to provide login and logout actions
class AuthController(object):
    def on_login(self, username = None):
        """Called on successful login"""
    
    def on_logout(self, username = None):
        """Called on logout"""
    
    def get_loginform(self,
                      username = None,
                      msg = "Enter login information",
                      from_page = "/"):
      return """<html><body>
          <form method="post" action="/auth/login">
          <input type="hidden" name="from_page" value="%(from_page)s" />
          %(msg)s<br />
          Username: <input type="text" name="username" value="%(username)s" /><br />
          Password: <input type="password" name="password" /><br />
          <input type="submit" value="Log in" />
      </body></html>""" % locals()
    
    @cherrypy.expose
    def login(self, username=None, password=None, from_page="/"):
      if username is None or password is None:
        return self.get_loginform("", from_page=from_page)
      
      error_msg = check_credentials(username, password)
      if error_msg:
        return self.get_loginform(username, error_msg, from_page)
      else:
        cherrypy.session[USERNAME_SESSION_KEY] = cherrypy.request.login = username
        self.on_login(username)
        raise cherrypy.HTTPRedirect(from_page or "/")
    
    @cherrypy.expose
    def logout(self, from_page="/"):
        sess = cherrypy.session
        username = sess.get(USERNAME_SESSION_KEY, None)
        sess[USERNAME_SESSION_KEY] = None
        if username:
            cherrypy.request.login = None
            self.on_logout(username)
        raise cherrypy.HTTPRedirect(from_page or "/")
